package com.baizw.cultivation.common.response;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baizw.cultivation.common.exception.GlobalException;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;


/**
 * 接口返回数据格式
 *
 * @author 剧终1204
 * @version V1.0
 * @date 2020年03月20日 17:54:43
 */
@Data
@Slf4j
@NoArgsConstructor
@Accessors(chain = true)
@ApiModel(value = "接口返回对象", description = "接口返回对象")
public class ResultBean<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 成功标志
     */
    @ApiModelProperty(value = "成功标志")
    private boolean success = true;

    /**
     * 返回处理消息
     */
    @ApiModelProperty(value = "返回处理消息")
    private String message = "操作成功！";

    /**
     * 返回代码
     */
    @ApiModelProperty(value = "返回代码")
    private Integer code = 0;

    /**
     * 返回数据对象 data
     */
    @ApiModelProperty(value = "返回数据对象")
    private T result;

    /**
     * 时间戳
     */
    @ApiModelProperty(value = "时间戳")
    private long timestamp = System.currentTimeMillis();

    /**
     * 返回成功消息
     *
     * @param message 消息
     * @return 消息对象
     */
    public ResultBean<T> success(String message) {
        this.message = message;
        this.code = HttpStatus.OK.value();
        this.success = true;
        return this;
    }

    /**
     * 初始化对象
     *
     * @return ResultBean
     */
    private static <T> ResultBean<T> build() {
        return new ResultBean<>();
    }

    /**
     * 返回成功消息
     *
     * @return ResultBean
     */
    public static <T> ResultBean<T> ok() {
        ResultBean<T> resultBean = build();
        return resultBean.success("成功");
    }

    /**
     * 返回成功消息
     *
     * @param message 消息
     * @return ResultBean
     */
    public static <T> ResultBean<T> ok(String message) {
        ResultBean<T> resultBean = build();
        return resultBean.success(message);
    }

    /**
     * 返回成功消息
     *
     * @param data 携带数据
     * @return ResultBean
     */
    public static <T> ResultBean<T> ok(T data) {
        ResultBean<T> resultBean = build();
        return resultBean.setResult(data);
    }

    /**
     * 返回失败消息
     *
     * @return ResultBean
     */
    public static <T> ResultBean<T> error() {
        return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "操作失败");
    }

    /**
     * 返回失败消息
     *
     * @param message 消息
     * @return ResultBean
     */
    public static <T> ResultBean<T> error(String message) {
        return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }

    /**
     * 返回失败消息
     *
     * @param message 消息
     * @return ResultBean
     */
    public static <T> ResultBean<T> error(int code, String message) {
        ResultBean<T> resultBean = build();
        return resultBean.setCode(code).setMessage(message).setSuccess(false);
    }

    /**
     * 返回失败消息
     *
     * @param message 消息
     * @return ResultBean
     */
    public ResultBean<T> error500(String message) {
        this.message = message;
        this.code = HttpStatus.INTERNAL_SERVER_ERROR.value();
        this.success = false;
        return this;
    }

    /**
     * 无权限访问返回结果
     */
    public static <T> ResultBean<T> authFailed(String msg) {
        return error(HttpStatus.UNAUTHORIZED.value(), msg);
    }

    /**
     * 根据表达式返回对应消息结果
     *
     * @param expression 表达式
     * @return ResultBean
     */
    public static <T> ResultBean<T> isTrue(boolean expression) {
        return expression ? ResultBean.ok() : ResultBean.error();
    }

    /**
     * 根据表达式返回对应消息结果
     *
     * @param expression 表达式
     * @param template   错误消息模板， {} 表示占位符<br> <font style="color:red">被替换的部分用 {} 表示</font>
     * @param params     参数
     * @return ResultBean
     */
    public static <T> ResultBean<T> isTrue(boolean expression, String template, Object... params) {
        return expression ? ResultBean.ok() : ResultBean.error(StrUtil.format(template, params));
    }

    /**
     * 根据表达式返回对应消息结果
     *
     * @param expression 表达式
     * @param data       成功后携带的数据
     * @param template   错误消息模板， {} 表示占位符<br> <font style="color:red">被替换的部分用 {} 表示</font>
     * @param params     参数
     * @return ResultBean
     */
    public static <T> ResultBean<T> isTrue(boolean expression, T data, String template, Object... params) {
        return expression ? ok(data) : error(StrUtil.format(template, params));
    }

    /**
     * 返回消息结果携带Map结果
     *
     * @param key   Map Key
     * @param value Map Value
     * @return ResultBean
     */
    public ResultBean put(String key, Object value) {
        Object result = this.getResult();
        Map<String, Object> convertResult = verifyAndConvertResult(result);
        convertResult.put(key, value);
        this.setResult((T) convertResult);
        return this;
    }
    
    /**
     * 返回消息结果携带Map结果
     *
     * @param key   Map Key
     * @param value Map Value
     * @return ResultBean
     */
    public static ResultBean<Map<String, Object>> successPut(String key, Object value) {
        Map<String, Object> result = MapUtil.newHashMap();
        result.put(key, value);
        return ok(result);
    }
    
    /**
     * 返回消息结果携带Map结果
     *
     * @param params Map
     * @return ResultBean
     */
    public ResultBean putAll(Map<String, Object> params) {
        Object result = this.getResult();
        Map<String, Object> convertResult = verifyAndConvertResult(result);
        convertResult.putAll(params);
        this.setResult((T) convertResult);
        return this;
    }

    /**
     * 校验Result参数类型
     *
     * @param result 泛型参数
     * @return Map<String, Object>
     */
    private static Map<String, Object> verifyAndConvertResult(Object result) {
        // 没有结果，创建空的Map等待接受参数
        if (ObjectUtil.isEmpty(result)) {
            result = new HashMap<>(16);
        }
        // 结果是不是是Map结构且内容不是空的则将内容转化为Map
        if (BooleanUtil.isFalse(result instanceof HashMap)) {
            log.error("返回结果参数类型不匹配");
            throw new GlobalException(500, "参数类型不匹配");
        }
        try {
            return Convert.convert(HashMap.class, result);
        } catch (ConvertException e) {
            log.error("参数类型转化异常", e);
            throw new GlobalException(500, "参数类型转化异常");
        }
    }

    /**
     * 返回错误携带消息的结果
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例：<br>
     * 通常使用：format("this is {} for {}", "a", "b") =》 this is a for b<br>
     * 转义{}： format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
     * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param params   参数值
     * @return javax.xml.transform.ResultBean
     */
    public static <T> ResultBean<T> successFormat(CharSequence template, Object... params) {
        if (null == template) {
            return ok();
        }
        if (ArrayUtil.isEmpty(params) || StrUtil.isBlank(template)) {
            return ok(template.toString());
        }
        return ok(StrFormatter.format(template.toString(), params));
    }

    /**
     * 返回错误携带消息的结果
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例：<br>
     * 通常使用：format("this is {} for {}", "a", "b") =》 this is a for b<br>
     * 转义{}： format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
     * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param params   参数值
     * @return ResultBean
     */
    public static <T> ResultBean<T> failureFormat(CharSequence template, Object... params) {
        if (null == template) {
            return error();
        }
        if (StrUtil.isBlank(template) || ArrayUtil.isEmpty(params)) {
            return error(template.toString());
        }
        return error(StrFormatter.format(template.toString(), params));
    }

}